﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.IO;
using System.Windows.Annotations;
using System.Windows.Annotations.Storage;
using System.Speech.Synthesis;
using Indigo.Quibbler.Data;
using System.Windows.Media.Imaging;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.Windows.Interop;
using System.Windows.Media;

namespace Indigo.Quibbler.Controls
{
    /// <summary>
    /// Interaction logic for ArticleReader.xaml
    /// </summary>
    public partial class ArticleReader : UserControl
    {
        #region Private Members
        private Stream _stream = null;
        private AnnotationService _service = null;
        private AnnotationStore _store = null;
        private SpeechSynthesizer _synth = null;
        private List<Block> _resetBlocks = new List<Block>();
        private bool _isScribbling = false;
        #endregion

        #region Properties
        public NewsArticle ItemSource
        {
            get { return (NewsArticle)GetValue(NewsArticleProperty); }
            set { SetValue(NewsArticleProperty, value); }
        }

        // Using a DependencyProperty as the backing store for NewsArticle.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty NewsArticleProperty =
            DependencyProperty.Register("ItemSource", typeof(NewsArticle), typeof(ArticleReader), new UIPropertyMetadata(null, OnNewsArticleChanged));



        public int ZoomLevel
        {
            get { return (int)GetValue(ZoomLevelProperty); }
            set { SetValue(ZoomLevelProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ZoomLevel.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ZoomLevelProperty =
            DependencyProperty.Register("ZoomLevel", typeof(int), typeof(ArticleReader), new UIPropertyMetadata(100, OnZoomLevelChanged));


        #endregion

        #region Public Events
        public event EventHandler Close;
        #endregion

        #region Ctor
        public ArticleReader()
        {
            InitializeComponent();
            _resetBlocks = flowDocument.Blocks.ToList();
            flowDocumentReader.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(flowDocumentReader_PreviewMouseLeftButtonUp);
        }

        #endregion

        #region Public Members
        public void Scribble()
        {
            flowDocument.Focus();
            _isScribbling = true;
            try
            {
                System.Windows.Controls.Image _scribbleCursor = new System.Windows.Controls.Image();
                _scribbleCursor.Stretch = Stretch.Uniform;
                _scribbleCursor.Source = new BitmapImage(new Uri("pack://application:,,/Quibbler - Universal News Reader;component/Images/HoverScribble.png", UriKind.RelativeOrAbsolute));
                _scribbleCursor.Width = 32;
                _scribbleCursor.Height = 32;
                flowDocument.Cursor = CursorHelper.CreateCursor(_scribbleCursor, 5, 5);
            }
            catch (Exception ex)
            {
                flowDocument.Cursor = Cursors.Pen;
            }
        }

        public void SpeakAloud()
        {
            if (_synth == null)
                _synth = new SpeechSynthesizer();
            if (_synth.State == SynthesizerState.Speaking)
            {
                _synth.SpeakAsyncCancelAll();
            }

            TextRange range = new TextRange(flowDocument.ContentStart, flowDocument.ContentEnd);
            _synth.Rate = Indigo.Quibbler.Properties.Settings.Default.ReadingSpeed;
            _synth.SpeakAsync(range.Text);
        }

        public void StopSpeaking()
        {
            if (_synth != null)
                _synth.SpeakAsyncCancelAll();
        }

        public void AddFeedback()
        {
            string FEEDBACK_URL = "http://your-domain-name-here/{0}.html#write";

            if (ItemSource != null)
            {
                string url = string.Format(FEEDBACK_URL, ItemSource.Id);
                System.Diagnostics.Process.Start(url);
            }
        }

        #endregion

        #region Event Handlers
        void flowDocumentReader_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if (_isScribbling && flowDocumentReader.Selection != null && !string.IsNullOrEmpty(flowDocumentReader.Selection.Text))
            {
                AnnotationHelper.CreateTextStickyNoteForSelection(_service, string.Empty);
                _isScribbling = false;
                flowDocument.Cursor = Cursors.Arrow;
            }
        }
        private static void OnZoomLevelChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            ArticleReader reader = (ArticleReader)sender;
            int zoomValue = (int)e.NewValue;

            if (zoomValue >= 60 && zoomValue <= 100)
                reader.flowDocumentReader.Zoom = zoomValue;
        }
        private void _CloseArticle(object sender, EventArgs e)
        {
            if (_synth != null && _synth.State == SynthesizerState.Speaking)
            {
                _synth.SpeakAsyncCancelAll();
            }

            if (Close != null)
            {
                Close(this, e);
            }
        }
        #endregion

        #region Helper Functions

        private Paragraph _GetParagraph(ArticleParagraph articleParagraph)
        {
            Paragraph para = new Paragraph();
            para.Inlines.Add(_GetRun(articleParagraph));
            return para;
        }

        private Run _GetRun(ArticleParagraph articleParagraph)
        {
            Run result = new Run();
            switch (articleParagraph.Style)
            {
                case ParagraphStyle.Italic:
                    result.FontStyle = FontStyles.Italic;
                    break;
                case ParagraphStyle.Bold:
                    result.FontWeight = FontWeights.Bold;
                    break;
                default:
                    break;
            }
            result.Text = articleParagraph.Text;
            return result;
        }

        private void _IntiailizeAnnotation()
        {
            try
            {
                if (this._service != null && this._service.IsEnabled)
                {
                    this._service.Disable();
                    this._stream.Close();
                }
                this._stream = new FileStream(_GetFileName(ItemSource.Id), FileMode.OpenOrCreate);
                this._service = new AnnotationService(this.flowDocumentReader);
                this._store = new XmlStreamStore(this._stream);
                this._store.AutoFlush = true;
                this._service.Enable(this._store);
            }
            catch (Exception)
            {
                MessageBox.Show("Cannot Initialize Annotation Service. Service will be not be available.");
            }
        }

        private string _GetFileName(string id)
        {
            string filename = Path.GetFileNameWithoutExtension(id);
            string datastorePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"Universal News Reader\DataStore\");
            string annotationsBaseDir = Path.Combine(datastorePath, "Annotations");

            if (string.IsNullOrEmpty(id)) return "annotations.xml";
            if (!Directory.Exists(annotationsBaseDir))
                Directory.CreateDirectory(annotationsBaseDir);
            string path = System.IO.Path.Combine(annotationsBaseDir, filename + ".xml");
            if (File.Exists(path))
                File.SetAttributes(path, FileAttributes.Normal);

            return path;
        }

        private string _ParseHTML(string html)
        {
            html = html.Replace("”", "\"");
            html = html.Replace("‘", "'");
            html = html.Replace("’", "'");
            html = html.Replace("�", "'");

            return html;
        }
        #endregion

        #region Dependency Member
        private static void OnNewsArticleChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            (obj as ArticleReader).OnNewsArticleChanged(e);
        }
        #endregion

        #region Dependency Member
        private void OnNewsArticleChanged(DependencyPropertyChangedEventArgs e)
        {
            try
            {
                _IntiailizeAnnotation();
                flowDocument.Blocks.Clear();
                flowDocument.Blocks.AddRange(_resetBlocks);
                List<string> category = new List<string>();
                if (!string.IsNullOrEmpty(ItemSource.Category))
                    category = ItemSource.Category.Split('/').ToList();
                if (category.Count >= 1)
                {
                    mainCategory.Text = category[0];
                    mainCategory.Visibility = Visibility.Visible;
                }
                else
                    mainCategory.Visibility = Visibility.Collapsed;
                if (category.Count >= 2)
                {
                    subCategory.Text = category[1];
                    subCategory.Visibility = Visibility.Visible;
                }
                else if (category.Count == 1)
                {
                    subCategory.Text = "Top Stories";
                    subCategory.Visibility = Visibility.Visible;
                }
                else
                    subCategory.Visibility = Visibility.Collapsed;
                if (!string.IsNullOrEmpty(ItemSource.Headline))
                {
                    headline.Text = ItemSource.Headline;
                }
                if (!string.IsNullOrEmpty(ItemSource.Rating))
                {
                    try
                    {
                        double rating = double.Parse(ItemSource.Rating);
                        if (rating > 0 && rating <= 5)
                        {
                            if (((rating * 10) % 5) != 0)
                            {
                                rating = Math.Round(rating, 0);
                            }
                            imgRating.Source = new BitmapImage(new Uri(String.Format("pack://application:,,/Quibbler - Universal News Reader;component/Images/{0}.png", rating.ToString()), UriKind.RelativeOrAbsolute));
                        }
                        else
                            imgRating.Source = null;
                    }
                    catch { imgRating.Source = null; }
                }
                else
                    imgRating.Source = null;
                if (!string.IsNullOrEmpty(ItemSource.PubDate))
                {
                    if (!string.IsNullOrEmpty(ItemSource.Author))
                        date_author.Text = ItemSource.Author + " " + ItemSource.PubDate;
                    else
                        date_author.Text = ItemSource.PubDate;
                }

                try
                {
                    if (!string.IsNullOrEmpty(ItemSource.ImageUrl))
                    {
                        imgArticle.Source = ItemSource.ImageUrl;
                        imgArticle.Visibility = Visibility.Visible;

                        figHeadline.Width = new FigureLength(1, FigureUnitType.Column);
                        figHeadline.Height = new FigureLength(.75, FigureUnitType.Column);
                    }
                    else
                    {
                        imgArticle.Source = null;
                        imgArticle.Visibility = Visibility.Collapsed;
                        figHeadline.Width = new FigureLength(0);
                        figHeadline.Height = new FigureLength(0);
                    }
                }
                catch { };

                if (ItemSource.Paragraphs.Count >= 1)
                {
                    if (firstPara.Inlines.Count >= 2)
                        firstPara.Inlines.Remove(firstPara.Inlines.ElementAt(firstPara.Inlines.Count - 1));
                    firstPara.Inlines.Add(_GetRun(ItemSource.Paragraphs[0]));
                    for (int i = 1; i < ItemSource.Paragraphs.Count; i++)
                    {
                        flowDocument.Blocks.Add(_GetParagraph(ItemSource.Paragraphs[i]));
                    }
                }
                flowDocumentReader.ViewingMode = FlowDocumentReaderViewingMode.Scroll;
                flowDocumentReader.ViewingMode = FlowDocumentReaderViewingMode.Page;
            }
            catch { }
        }
        #endregion
    }

    #region Helper Cursor Class
    public class CursorHelper
    {
        private struct IconInfo
        {
            public bool fIcon;
            public int xHotspot;
            public int yHotspot;
            public IntPtr hbmMask;
            public IntPtr hbmColor;
        }

        [DllImport("user32.dll")]
        private static extern IntPtr CreateIconIndirect(ref IconInfo icon);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool GetIconInfo(IntPtr hIcon, ref IconInfo pIconInfo);


        private static Cursor InternalCreateCursor(System.Drawing.Bitmap bmp,
            int xHotSpot, int yHotSpot)
        {
            IconInfo tmp = new IconInfo();
            GetIconInfo(bmp.GetHicon(), ref tmp);
            tmp.xHotspot = xHotSpot;
            tmp.yHotspot = yHotSpot;
            tmp.fIcon = false;

            IntPtr ptr = CreateIconIndirect(ref tmp);
            SafeFileHandle handle = new SafeFileHandle(ptr, true);
            return CursorInteropHelper.Create(handle);
        }

        public static Cursor CreateCursor(UIElement element, int xHotSpot, int yHotSpot)
        {
            element.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
            element.Arrange(new Rect(0, 0, element.DesiredSize.Width,
                element.DesiredSize.Height));

            RenderTargetBitmap rtb = new RenderTargetBitmap((int)element.DesiredSize.Width,
                (int)element.DesiredSize.Height, 96, 96, PixelFormats.Pbgra32);
            rtb.Render(element);

            PngBitmapEncoder encoder = new PngBitmapEncoder();
            encoder.Frames.Add(BitmapFrame.Create(rtb));

            MemoryStream ms = new MemoryStream();
            encoder.Save(ms);

            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(ms);

            ms.Close();
            ms.Dispose();

            Cursor cur = InternalCreateCursor(bmp, xHotSpot, yHotSpot);

            bmp.Dispose();

            return cur;
        }
    }
    #endregion
}
